/*
 *  linux/fs/truncate.c
 *
 *  (C) 1991  Linus Torvalds
 */

#include <linux/sched.h>						// 调度程序头文件，定义了任务结构 task_struct、任务 0 数据等

#include <sys/stat.h>							// 文件状态头文件。含有文件或文件系统状态结构 stat{}和常量

//// 释放所有一次间接块。（内部函数）
// 参数 dev 是文件系统所在设备的设备号；block 是逻辑块号。成功则返回 1，否则返回 0。
static int free_ind(int dev,int block)
{
	struct buffer_head * bh;
	unsigned short * p;
	int i;
	int block_busy;								// 有逻辑块没有被释放的标志

	// 首先判断参数的有效性。如果逻辑块号为 0，则返回。然后读取一次间接块，并释放其上表
	// 明使用的所有逻辑块，然后释放该一次间接块的缓冲块。 函数 free_block()用于释放设备
	// 上指定逻辑块号的磁盘块。
	if (!block)
		return 1;
	block_busy = 0;
	if (bh=bread(dev,block)) {
		p = (unsigned short *) bh->b_data;		// 指向缓冲块数据区
		for (i=0;i<512;i++,p++)					// 每个逻辑块上可有 512 个块号
			if (*p)
				if (free_block(dev,*p)) {		// 释放指定的设备逻辑块		
					*p = 0;						// 清零
					bh->b_dirt = 1;				// 设置已修改标志
				} else
					block_busy = 1;				// 设置逻辑块没有释放标志
		brelse(bh);								// 然后释放间接块占用的缓冲块
	}

	// 最后释放设备上的一次间接块。但如果其中有逻辑块没有被释放，则返回 0（失败）
	if (block_busy)
		return 0;
	else
		return free_block(dev,block);			// 成功则返回 1，否则返回 0
}

//// 释放所有二次间接块。
// 参数 dev 是文件系统所在设备的设备号；block 是逻辑块号。
static int free_dind(int dev,int block)
{
	struct buffer_head * bh;
	unsigned short * p;
	int i;
	int block_busy;								// 有逻辑块没有被释放的标志

	// 首先判断参数的有效性。如果逻辑块号为 0，则返回。然后读取二次间接块的一级块，并释放
 	// 其上表明已使用的所有逻辑块，然后释放该一级块的缓冲块。
	if (!block)
		return 1;
	block_busy = 0;
	if (bh=bread(dev,block)) {
		p = (unsigned short *) bh->b_data;		// 指向缓冲块数据区
		for (i=0;i<512;i++,p++)					// 每个逻辑块上可连 512 个二级块
			if (*p)
				if (free_ind(dev,*p)) {			// 释放所有一次间接块
					*p = 0;						// 清零
					bh->b_dirt = 1;				// 设置已修改标志
				} else
					block_busy = 1;				// 设置逻辑块没有释放标志
		brelse(bh);								// 释放二次间接块占用的缓冲块
	}

	// 最后释放设备上的二次间接块。但如果其中有逻辑块没有被释放，则返回 0（失败）
	if (block_busy)
		return 0;
	else
		return free_block(dev,block);
}

//// 截断文件数据函数。
// 将节点对应的文件长度截为 0，并释放占用的设备空间。
void truncate(struct m_inode * inode)
{
	int i;
	int block_busy;								// 有逻辑块没有被释放的标志

	// 首先判断指定 i 节点的有效性。如果不是常规文件、目录文件或链接项，则返回。
	if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
	     S_ISLNK(inode->i_mode)))
		return;
repeat:
	// 然后释放 i 节点的 7 个直接逻辑块，并将这 7 个逻辑块项全置零。函数 free_block()用于
	// 释放设备上指定逻辑块号的磁盘块。若有逻辑块忙而没有被释放则置块忙标志 block_busy。
	block_busy = 0;
	for (i=0;i<7;i++)
		if (inode->i_zone[i]) {					// 如果块号不为 0，则释放之
			if (free_block(inode->i_dev,inode->i_zone[i]))
				inode->i_zone[i]=0;				// 块指针置 0
			else
				block_busy = 1;					// 若没有释放掉则置标志
		}
	
	if (free_ind(inode->i_dev,inode->i_zone[7]))// 释放所有一次间接块
		inode->i_zone[7] = 0;					// 块指针置 0
	else
		block_busy = 1;							// 若没有释放掉则置标志
	if (free_dind(inode->i_dev,inode->i_zone[8])) // 释放所有二次间接块
		inode->i_zone[8] = 0;					// 块指针置 0
	else
		block_busy = 1;							// 若没有释放掉则置标志

	// 此后设置 i 节点已修改标志，并且如果还有逻辑块由于“忙”而没有被释放，则把当前进程
	// 运行时间片置 0，以先切换到其他进程去运行，稍等一会再重新执行释放操作。
	// 最后把文件修改时间和 i 节点改变时间设置为当前时间。宏 CURRENT_TIME 定义在头文件
	// linux/sched.h 中定义为(startup_time + jiffies/HZ)。以取得从 1970:0:0:0
	// 开始到现在为止经过的秒数。
	inode->i_dirt = 1;
	if (block_busy) {
		current->counter = 0;					// 当前进程时间片置 0
		schedule();
		goto repeat;
	}
	inode->i_size = 0;							// 文件大小置零
	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
}

